﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Collections;

namespace IBNet.UtilsDataStructuresAndExtensions
{
   /// <summary>
   /// A thread safe hash set.
   /// </summary>
   /// <typeparam name="T">The type of elements in the hash set.</typeparam>
   public class ThreadSafeHashSet<T> : IEnumerable<T>, IDisposable
   {
      private readonly HashSet<T> hashset = new HashSet<T>();
      private readonly ReaderWriterLockSlim hashsetLock = new ReaderWriterLockSlim();
      private bool disposed;
      
      /// <summary>
      /// Adds the specified element to the set.
      /// </summary>
      /// <param name="item">The element to add to the set.</param>
      /// <returns>true if the element is added to the ThreadSafeHashSet&lt;T>; false if the element is already present.</returns>
      public bool Add(T item)
      {
         bool itemWasAddedSuccessfully;
         hashsetLock.EnterUpgradeableReadLock();
         try
         {
            if (!hashset.Contains(item))
            {
               hashsetLock.EnterWriteLock();
               try
               {
                  hashset.Add(item);
                  itemWasAddedSuccessfully = true;
               }
               finally
               {
                  hashsetLock.ExitWriteLock();
               }
            }
            else
            {
               itemWasAddedSuccessfully = false;
            }
         }
         finally
         {
            hashsetLock.ExitUpgradeableReadLock();
         }
         return itemWasAddedSuccessfully;
      }

      /// <summary>
      /// Determines whether an element is in the ThreadSafeHashSet&lt;T>.
      /// </summary>
      /// <param name="item">The element to locate in the ThreadSafeHashSet&lt;T>.</param>
      /// <returns>true if the ThreadSafeHashSet&lt;T> contains the specified element; otherwise, false.</returns>
      public bool Contains(T item)
      {
         bool doesContainItem;
         hashsetLock.EnterReadLock();
         try
         {
            doesContainItem = hashset.Contains(item);
         }
         finally
         {
            hashsetLock.ExitReadLock();
         }
         return doesContainItem;
      }

      /// <summary>
      /// Removes the specified element from the ThreadSafeHashSet&lt;T>.
      /// </summary>
      /// <param name="item">The element to remove.</param>
      /// <returns>true if the item was successfully removed; otherwise, false. This method returns false if item is not found in the ThreadSafeHashSet&lt;T>.</returns>
      public bool Remove(T item)
      {
         bool itemWasRemoved;
         hashsetLock.EnterUpgradeableReadLock();
         try
         {
            if (hashset.Contains(item))
            {
               itemWasRemoved = true;
               hashsetLock.EnterWriteLock();
               try
               {
                  hashset.Remove(item);
               }
               finally
               {
                  hashsetLock.ExitWriteLock();
               }
            }
            else
            {
               itemWasRemoved = false;
            }
         }
         finally
         {
            hashsetLock.ExitUpgradeableReadLock();
         }
         return itemWasRemoved;
      }

      /// <summary>
      /// Removes all elements from the ThreadSafeHashSet&lt;T>.
      /// </summary>
      public void Clear()
      {
         hashsetLock.EnterUpgradeableReadLock();
         try
         {
            if (hashset.Count > 0)
            {
               hashsetLock.EnterWriteLock();
               try
               {
                  hashset.Clear();
               }
               finally
               {
                  hashsetLock.ExitWriteLock();
               }
            }   
         }
         finally
         {
            hashsetLock.ExitUpgradeableReadLock();
         }
      }

      /// <summary>
      /// Gets the number of elements that are contained in the ThreadSafeHashSet&lt;T>.
      /// </summary>
      public int Count
      {
         get
         {
            int count;
            hashsetLock.EnterReadLock();
            try
            {
               count = hashset.Count;
            }
            finally
            {
               hashsetLock.ExitReadLock();
            }
            return count;
         }
      }

      /// <summary>
      /// Returns an enumerator that iterates through the ThreadSafeHashSet&lt;T>.
      /// </summary>
      /// <returns></returns>
      public IEnumerator<T> GetEnumerator()
      {
         return hashset.GetEnumerator();
      }

      /// <summary>
      /// Returns an enumerator that iterates through the ThreadSafeHashSet&lt;T>.
      /// </summary>
      /// <returns></returns>
      IEnumerator IEnumerable.GetEnumerator()
      {
         return hashset.GetEnumerator();
      }

      /// <summary>
      /// Disposes of the internal ReaderWriterLockSlim instance.
      /// </summary>
      public void Dispose()
      {
         Dispose(true);
         GC.SuppressFinalize(this);
      }

      protected virtual void Dispose(bool disposing)
      {
         if (disposed) return;

         if (disposing)
         {
            if (hashsetLock != null)
            {
               hashsetLock.Dispose();
               disposed = true;
            }
         }
      }
   }
}